package se581.banker.proxy;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

import org.apache.log4j.Logger;

import se581.banker.SimulationClientMain;
import se581.banker.event.TrackerEventWrapper;
import se581.banker.manager.AccountTracker;
import se581.banker.manager.TrackerObserver;
import se581.banker.util.ServerSocketInfo;

/**
 Proxy to abstract the update client from the UpdateTracker  
 * communication logic
 * 
 * @author mmichalak
 *
 */
public class UpdateProxy implements TrackerProxy, TrackerObserver {
	static Logger logger = Logger.getLogger(UpdateProxy.class.getName());
	int MAX_NUM_SOCKET_RETRIES = 2;
	
	String myHost = null;
	String myPort = null;

	
	/*
	 * This is the server info used by this proxy
	 */
	ServerSocketInfo serverSocketInfo = null;
	int openSocketFails = 0;
	
	
	public UpdateProxy(String myHost, String myPort) {
		super();
		this.myHost = myHost;
		this.myPort = myPort;
	}
	
	
	public void processRequest(TrackerEventWrapper evWrapper) {
		logger.debug("Beginning send to UpdateTracker Server");
		
		if (evWrapper == null) {
			logger.error("ERROR - TrackerEventWrapper is null");
			return;
		}
		else if (evWrapper.getEvent() == null) {
			logger.error("TrackerEventWrapper is null or the TrackerEvent is null:  " + evWrapper.toString());
			evWrapper.setServerSuccess(false);
			if (evWrapper != null)
				evWrapper.setServerReturn("ERROR - Event is missing from EventWrapper.");
			return;
		}

		if (serverSocketInfo == null || openSocketFails == MAX_NUM_SOCKET_RETRIES)
			registerWithRegistrationSvc(myHost, myPort); // sets to class var serverSocketInfo
		

		String socketHostPort = null;
		Socket clientSocket = null;
		DataOutputStream outToServer = null;
		BufferedReader inFromServer = null;
		try {
			clientSocket = new Socket( serverSocketInfo.getHost(), Integer.parseInt(serverSocketInfo.getPort()) );
	
			outToServer =
			     new DataOutputStream(
			     clientSocket.getOutputStream());
			
			inFromServer =
			     new BufferedReader(
			     new InputStreamReader(
			     clientSocket.getInputStream()));
  
		} catch (Exception e) {
			logger.error("Error opening client socket to localhost port " + socketHostPort);
			//e.printStackTrace(); // May comment out at some point
			evWrapper.setServerSuccess(false);
			evWrapper.setServerReturn("Error establishing connection to server - " + e.getMessage());
			
			// Close socket in case still open (probably not...)
			try { clientSocket.close(); } catch (Exception ex) {
				logger.error("Error closing socket in finally:  " + ex.getMessage() );
			}
			openSocketFails++;
			
			return;
		}
		
		String retFromServer;
		
		try {

			outToServer.writeBytes(evWrapper.getEvent().toEventLine() + '\n');
			
			retFromServer = inFromServer.readLine();
			logger.debug("FROM SERVER: " + retFromServer);
			evWrapper.setServerReturn(retFromServer);

			clientSocket.close();
		} catch (Exception e) {
			evWrapper.setServerSuccess(false);
			evWrapper.setServerReturn("Error communicating with server: " + socketHostPort);
			openSocketFails++;
			//e.printStackTrace();
		} finally {
			try { clientSocket.close(); } catch (IOException e) {
				logger.error("Error closing socket in finally....Oooops, socket leak");
			}
		}
		
        return;
	}
	
	/**
	 * Returns the socket connection host:port for UPDATE server communication
	 * 
	 * Request:  UPDATE,localhost:8878
	 * Response:
	 * 
	 * @param myHost
	 * @param myPort
	 */
	void registerWithRegistrationSvc(String myHost, String myPort) {
		logger.debug("Registering UPDATE with RegistrationService....");
		
		if (myHost == null || myHost.isEmpty() || myPort == null || myPort.isEmpty())
			logger.error("ERROR registering with Update Server.  myHost and/or myPort is null or empyty.  myHost:myPort = " + myHost + ":" + myPort);
		
		openSocketFails = 0; // reset
		
	      Socket clientSocket = null;
	      try {
	    	  clientSocket = new Socket( 
								SimulationClientMain.registrationServerInfo.getHost(),
								Integer.parseInt(SimulationClientMain.registrationServerInfo.getPort()) );
				  
			  DataOutputStream outToServer =
			     new DataOutputStream(
			     clientSocket.getOutputStream());

			  BufferedReader inFromServer =
			     new BufferedReader(
			     new InputStreamReader(
			     clientSocket.getInputStream()));

			  outToServer.writeBytes("UPDATE," + myHost + ":" + myPort + '\n');

			  String updateSvrInfoString = inFromServer.readLine();
			  serverSocketInfo = new ServerSocketInfo(updateSvrInfoString);
			  logger.debug("FROM SERVER: Update Server:port=" + serverSocketInfo.toString());

		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			logger.error("Unknown UpdateProxy error.");
			e.printStackTrace();
		} finally {
			try { clientSocket.close(); } catch (IOException e) {
				logger.error("Error closing socket in finally....Oooops, socket leak");
			}
		}
	 
	}

	/**
	 * AccountTracker Failover observer callback method
	 */
	public void updateTracker(AccountTracker accountTracker) {
//		aTracker = accountTracker;	
	}

}
